//observe监听器：数据劫持/数据响应式

import Observer from "./observer";

//初始化状态
export function initState(vm) {

  /**
   * console.log(vm);
   * Vue {
   *   $options: {el: '#app', computed: {…}, watch: {…}, data: ƒ}
   * }
   */

  //拿到配置对象项
  let options = vm.$options;

  if (options.data) {
    initData(vm);
  }

  if (options.computed) {
    initComputed(vm);
  }

  if (options.watch) {
    initWatch(vm);
  }
}

//重新定义data
export function observe(data) {
  //保证data是对象且有内容，否则中止
  if (typeof data !== 'object' || data == null) {
    return;
  }

  //证明已经观察过了，执行过observe方法
  if (data.__ob__) {
    //不需要重复观察
    return data.__ob__;
  }

  //Observer这个类帮忙观察数据
  return new Observer(data);

}

//初始化数据
function initData(vm) {
  //给数据重新定义，并变为响应式属性
  let data = vm.$options.data;
  /**
   * console.log(data);
   * ƒ data() {
   *   return {
   *     message: 'hello vue2.x',
   *     nums: [1, 4, 6],
   *     person: {
   *       name: 'mike',
   *       age: 20
   *     }
   *   }
   * }
   */

  //传入的data有可能是一个对象，也有可能是一个没有执行的函数
  //vm._data：赋值给data的副本避免操作原来data的引用
  //保证data始终有值
  data = vm._data = typeof data === 'function' ? data.call(vm) : data || {};

  //对数据进行代理：减短data访问的代码长度: 访问vm._data.message代理为vm.message
  for (let key in data) {
    // console.log(vm);
    //Vue {$options: {…}, _data: {…}}

    // console.log(key);
    //message/nums/person

    //proxy(实例,需去除的片段,属性名)
    proxy(vm, '_data', key);
  }

  //观察数据：重新定义data(加入getter/setter), 传入data副本
  observe(vm._data);

}

function initComputed(vm) {}

function initWatch(vm) {
  let watch = vm.$options.watch;
  // console.log(watch);
  //{message: ƒ, number: ƒ}

  //循环对象里的每一项(键值对)
  for (let key in watch) {
    //handler => 事件处理函数
    let handler = watch[key];
    // console.log(handler);
    // ƒ / ƒ

    createWatcher(vm, key, handler);
  }
}

//返回挂载在vm实例 $watch方法的结果
function createWatcher(vm, key, handler) {
  return vm.$watch(key, handler);
}

//代理路径修改
function proxy(vm, source, key) {
  Object.defineProperty(vm, key, {
    get() {
      //相当于访问vm._data.message
      return vm[source][key];
    },
    set(newValue) {
      //相当于vm._data.message = 'xxx'
      vm[source][key] = newValue;
    }
  });
}